import { createApp } from './app'

const isDev = process.env.NODE_ENV !== 'production'

// 这个导出的函数将被' bundleRenderer '调用(隐式调用，应该是最后基于该入口文件
// 打包生成的文件)被renderer渲染器读取到打包后的信息后，隐式调用该入口文件开启服务端渲染过程
// 这是我们在实际呈现应用程序之前执行数据预取以确定其状态的地方。
// 由于数据获取是异步的，所以这个函数应该返回一个解析到应用实例的Promise
// 即会等到vue-router加载到所有当前路由需要用到的异步组件时，然后会异步执行各个路由组件的
// asyncData方法进行数据预取，所有异步任务做完之后store中的数据已经填充好了，这
// 才return app实例对所有用到的组件实例化，所以这也是为什么有的组件直接在data中使用store数据
export default context => {
  return new Promise((resolve, reject) => {
    const s = isDev && Date.now()
    // 创建vue的app实例、vue-router实例、vuex实例
    // 每次有新的请求到服务端都会创建
    const { app, router, store } = createApp()
    
    const { url } = context // context就是上面server.js文件中传递给渲染器的对象
    const { fullPath } = router.resolve(url).route

    if (fullPath !== url) {
      return reject({ url: fullPath })
    }
 
    // set router's location
    router.push(url) // 让vue-router去加载当前url对应需要用到的路由组件

    // wait until router has resolved possible async hooks
    // 把需要的异步路由组件都加载了...
    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      // no matched routes
      if (!matchedComponents.length) {
        return reject({ code: 404 })
      }
      // Call fetchData hooks on components matched by the route.
      // A preFetch hook dispatches a store action and returns a Promise,
      // which is resolved when the action is complete and store state has been
      // updated.
      Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({
        store,
        route: router.currentRoute
      }))).then(() => {
        isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
        // After all preFetch hooks are resolved, our store is now
        // filled with the state needed to render the app.
        // Expose the state on the render context, and let the request handler
        // inline the state in the HTML response. This allows the client-side
        // store to pick-up the server-side state without having to duplicate
        // the initial data fetching on the client.
        // 把store加载好的state数据存入上下文对象中，后续渲染器在创建最终html文件的时候
        // 会把这个对象数据存入window.__state__中，让客户端直接读取并映射到客户端store上
        context.state = store.state
        resolve(app) // 完成所有数据预取后才返回app实例，做组件最终的实例化渲染工作
      }).catch(reject)
    }, reject)
  })
}
